package com.osmino.contacts.provider;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Message;
import android.provider.ContactsContract;
import com.osmino.contacts.entity.Contact;
import com.osmino.contacts.entity.Group;
import com.osmino.contacts.entity.QueryRestrictions;

public final class ContactsContractResolver implements Handler.Callback {

	private final Context mContext;
	protected final Handler mHandler;
	private final ContactsContentObserver mContentObserver;
	private final List<ContactsDataObserver> mListContactsDataObservers;
	private Map<Long, Contact> mMapContacts;
	private Map<Long, Group> mMapGroups;
	private List<Contact> mListContacts;
	private List<Group> mListGroups;
	private QueryRestrictions mQueryRestrictions;
	protected AsyncContactsCursorLoader mCursorLoader;
	private static final int MESSAGE_DATA_SHOULD_UPDATE = 1;
	private boolean mCreated;

	public ContactsContractResolver(Context context) {
		mContext = context;
		mHandler = new Handler(this);
		mContentObserver = new ContactsContentObserver(mHandler);
		ContentResolver contentResolver = mContext.getContentResolver();
		Uri authorityUri = ContactsContract.AUTHORITY_URI;
		contentResolver.registerContentObserver(authorityUri, true, mContentObserver);
		mListContactsDataObservers = new ArrayList<ContactsContractResolver.ContactsDataObserver>();
		mMapContacts = new HashMap<Long, Contact>();
		mMapGroups = new HashMap<Long, Group>();
		mCreated = false;
	}

	public void create() {
		reset();
		mCreated = true;
	}

	public boolean isCreated() {
		return mCreated;
	}

	public void destroy() {
		mHandler.removeCallbacksAndMessages(null);
		ContentResolver contentResolver = mContext.getContentResolver();
		contentResolver.unregisterContentObserver(mContentObserver);
		mListContactsDataObservers.clear();
		if (mCursorLoader != null) {
			mCursorLoader.cancel(true);
		}
	}

	public Context getContext() {
		return mContext;
	}

	public QueryRestrictions getQueryRestrictions() {
		return mQueryRestrictions;
	}

	public void setQueryRestrictions(QueryRestrictions queryRestrictions) {
		mQueryRestrictions = queryRestrictions;
	}

	public boolean isContactListsReady() {
		boolean busy = (mCursorLoader != null) && (mCursorLoader.getStatus() == AsyncTask.Status.RUNNING);
		return !busy;
	}

	private void addContactToGroup(Contact contact, long groupId) {
		Group group = mMapGroups.get(groupId);
		group.addContact(contact);
	}

	public List<Contact> getListContacts() {
		return mListContacts;
	}

	public List<Group> getListGroups() {
		return mListGroups;
	}

	public Map<Long, Contact> getMapContacts() {
		return mMapContacts;
	}

	public Map<Long, Group> getMapGroups() {
		return mMapGroups;
	}

	public Contact getContactById(long id) {
		return mMapContacts.get(id);
	}

	public void registerContactsDataObserver(ContactsDataObserver observer) {
		mListContactsDataObservers.add(observer);
	}

	public void unregisterContactsDataObserver(ContactsDataObserver observer) {
		mListContactsDataObservers.remove(observer);
	}

	public void onStartRefresh() {
		for (ContactsDataObserver observer : mListContactsDataObservers) {
			observer.onStartRefresh();
		}
	}

	public void onDataChanged(Map<Long, Contact> contactsMap, Map<Long, Group> mapGroups) {
		mMapContacts.clear();
		mMapGroups.clear();
		System.gc();
		mMapContacts = contactsMap;
		mMapGroups = mapGroups;
		mListContacts = new ArrayList<Contact>(mMapContacts.values());
		for (Contact contact : mListContacts) {
			int groupsSize = contact.getGroupIds().size();
			if (groupsSize == 0) {
				addContactToGroup(contact, Group.NO_GROUP);
			} else {
				for (int i = 0; i < groupsSize; i++) {
					addContactToGroup(contact, contact.getGroupIds().get(i));
				}
			}
		}
		mListGroups = new ArrayList<Group>(mMapGroups.values());
		for (ContactsDataObserver observer : mListContactsDataObservers) {
			observer.onRefreshContactsSctructure();
		}
	}

	@Override
	public boolean handleMessage(Message msg) {
		switch (msg.what) {
			case MESSAGE_DATA_SHOULD_UPDATE:
				launchLoader();
				break;
		}
		return false;
	}

	private void launchLoader() {
		if (mCursorLoader != null) {
			mCursorLoader.cancel(true);
		}
		mCursorLoader = new AsyncContactsCursorLoader(ContactsContractResolver.this);
		mCursorLoader.execute();
	}

	public void reset() {
		mQueryRestrictions = new QueryRestrictions(mContext);
		launchLoader();
		mHandler.sendEmptyMessage(MESSAGE_DATA_SHOULD_UPDATE);
	}

	private class ContactsContentObserver extends ContentObserver {

		public ContactsContentObserver(Handler handler) {
			super(handler);
		}

		@Override
		public void onChange(boolean selfChange) {
			super.onChange(selfChange);
			mHandler.removeMessages(MESSAGE_DATA_SHOULD_UPDATE);
			mHandler.sendEmptyMessageDelayed(MESSAGE_DATA_SHOULD_UPDATE, 200);
		}
	}

	public static interface ContactsDataObserver {

		public void onStartRefresh();

		public void onRefreshContactsSctructure();
	}
}
